﻿
/***************************************************************************

Copyright (c) BizRulX. All rights reserved.

***************************************************************************/

using System;
using System.Reflection;
using System.IO;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Collections.Generic;
using System.Configuration;
using System.Text;
using Microsoft.VisualStudio.Designer.Interfaces;
using Microsoft.VisualStudio.Shell;
using VSOLE = Microsoft.VisualStudio.OLE.Interop;
using System.Diagnostics;
using Microsoft.VisualStudio.Shell.Interop;
using EnvDTE;

namespace RuleXSoft.BizRuleCreator
{
    public class ClassGenerator : BaseCodeGenerator
    {
        /// <summary>
        /// Define the compile unit to use for code generation. 
        /// </summary>
        CodeCompileUnit targetUnit;

        /// <summary>
        /// The only class in the compile unit. This class contains 2 fields,
        /// 3 properties, a constructor, an entry point, and 1 simple method. 
        /// </summary>
        CodeTypeDeclaration targetClass;

        /// <summary>
        /// The name of the file to contain the source code.
        /// </summary>
        //private string outputFileName = ConfigurationSettings.AppSettings["outputAttributeFileName"].ToString();

        /// <summary>
        /// Define the class.
        /// </summary>
        public ClassGenerator(string strNameSpace, string className, string projectType)
        {
            targetUnit = new CodeCompileUnit();
            
            System.CodeDom.CodeNamespace clsNameSpace = new System.CodeDom.CodeNamespace(strNameSpace);
            clsNameSpace.Imports.Add(new CodeNamespaceImport("System"));
            clsNameSpace.Imports.Add(new CodeNamespaceImport("System.Collections"));
            clsNameSpace.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
            clsNameSpace.Imports.Add(new CodeNamespaceImport("ExcelReading"));
            targetClass = new CodeTypeDeclaration(className);
            targetClass.IsClass = true;
            targetClass.TypeAttributes = TypeAttributes.Public;

            if (projectType.Equals(".csproj"))
            {
                targetClass.BaseTypes.Add(new CodeTypeReference("ValidationAttributes<" + className + ">")); 
            }
            else if (projectType.Equals(".vbproj"))
            {
                targetClass.BaseTypes.Add(new CodeTypeReference("ValidationAttributes(Of " + className + ")"));
            }
            else if (projectType.Equals(".jsproj"))
            {
                targetClass.BaseTypes.Add(new CodeTypeReference("ValidationAttributes<" + className + ">"));
            }

            targetClass.BaseTypes.Add(new CodeTypeReference("IComparer"));
            clsNameSpace.Types.Add(targetClass);
            targetUnit.Namespaces.Add(clsNameSpace);

            

        }

        /// <summary>
        /// Adds two fields to the class.
        /// </summary>
        public void AddFields(string fieldName, Type dataType)
        {
            // Declare the fieldName.
            CodeMemberField fieldValue = new CodeMemberField();
            fieldValue.Attributes = MemberAttributes.Private;
            fieldValue.Name = fieldName;
            fieldValue.Type = new CodeTypeReference(dataType);
            //fieldValue.Comments.Add(new CodeCommentStatement(
            //    "The width of the object."));
            targetClass.Members.Add(fieldValue);

        }
        /// <summary>
        /// Add three properties to the class.
        /// </summary>
        public void AddProperties(string propertyName, string fieldName, Type dataType, bool direction)
        {
            // Declare the read-only Width property.
            CodeMemberProperty attributeProperty = new CodeMemberProperty();
            attributeProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            attributeProperty.Name = propertyName;
            attributeProperty.HasGet = true;
            attributeProperty.HasSet = true;
            attributeProperty.Type = new CodeTypeReference(dataType);

            attributeProperty.GetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), fieldName)));

 
            attributeProperty.SetStatements.Add(new CodeAssignStatement(
                new CodeFieldReferenceExpression(
                    new CodeThisReferenceExpression(), fieldName), new CodePropertySetValueReferenceExpression()));

            if (!direction)
            {
                attributeProperty.CustomAttributes.Add(
                          new CodeAttributeDeclaration("Direction",
                          new CodeAttributeArgument("Output", new CodePrimitiveExpression(Convert.ToBoolean("false")))));

            }
            else
            {
                attributeProperty.CustomAttributes.Add(
                          new CodeAttributeDeclaration("Direction",
                          new CodeAttributeArgument("Output", new CodePrimitiveExpression(Convert.ToBoolean("true")))));
            }
            targetClass.Members.Add(attributeProperty);
        }

        /// <summary>
        /// Adds a method to the class. This method multiplies values stored 
        /// in both fields.
        /// </summary>
        public void AddMethod(List<string> propNameList, string className, string projType)
        {
            // Declaring a ToString method
            CodeMemberMethod toCompareMethod = new CodeMemberMethod();
            toCompareMethod.Attributes =
                MemberAttributes.Public | MemberAttributes.Final;
            toCompareMethod.Name = "Compare";
            toCompareMethod.ReturnType =
                new CodeTypeReference(typeof(int));

            CodeParameterDeclarationExpression param1 = new CodeParameterDeclarationExpression(typeof(object), "x");
            CodeParameterDeclarationExpression param2 = new CodeParameterDeclarationExpression(typeof(object), "y");

            toCompareMethod.Parameters.Add(param1);
            toCompareMethod.Parameters.Add(param2);
 
            StringBuilder sbCodeConditionExpr = new StringBuilder();

            foreach (string item in propNameList)
            {
                IList<string> nameAndDirection = item.Split('|');

                if (!nameAndDirection[1].ToUpper().Equals("OUTPUT"))
                {
                    sbCodeConditionExpr.Append("(");
                    sbCodeConditionExpr.Append(nameAndDirection[0]);
                    sbCodeConditionExpr.Append(".Equals(((");
                    sbCodeConditionExpr.Append(className);
                    sbCodeConditionExpr.Append(")y).");
                    sbCodeConditionExpr.Append(nameAndDirection[0]);
                    sbCodeConditionExpr.Append("))");
                    sbCodeConditionExpr.Append(" && "); 
                }
            }

            sbCodeConditionExpr = sbCodeConditionExpr.Remove(sbCodeConditionExpr.Length - 4,4);
            CodeConditionStatement codeCondStatement = null;

            if (projType.Equals(".csproj"))
            {
                codeCondStatement = new CodeConditionStatement(
                        new CodeSnippetExpression(sbCodeConditionExpr.ToString()),
                        new CodeStatement[] { new CodeSnippetStatement("return 1;") },
                        new CodeStatement[] { new CodeSnippetStatement("return 0;") }); 
            }
            else if (projType.Equals(".vbproj"))
            {
                codeCondStatement = new CodeConditionStatement(
                        new CodeSnippetExpression(sbCodeConditionExpr.ToString()),
                        new CodeStatement[] { new CodeSnippetStatement("return 1") },
                        new CodeStatement[] { new CodeSnippetStatement("return 0") });
            }
            else if (projType.Equals(".jsproj"))
            {
                codeCondStatement = new CodeConditionStatement(
                        new CodeSnippetExpression(sbCodeConditionExpr.ToString()),
                        new CodeStatement[] { new CodeSnippetStatement("return 1;") },
                        new CodeStatement[] { new CodeSnippetStatement("return 0;") });
            }


            toCompareMethod.Statements.Add(codeCondStatement);
           
            targetClass.Members.Add(toCompareMethod);
        }
        /// <summary>
        /// Add a constructor to the class.
        /// </summary>
        public void AddConstructor()
        {
            // Declare the constructor
            CodeConstructor constructor = new CodeConstructor();
            constructor.Attributes =
                MemberAttributes.Public | MemberAttributes.Final;

            // Add parameters.
            //constructor.Parameters.Add(new CodeParameterDeclarationExpression(
            //    typeof(System.Double), "width"));
            //constructor.Parameters.Add(new CodeParameterDeclarationExpression(
            //    typeof(System.Double), "height"));

            // Add field initialization logic
            //CodeFieldReferenceExpression widthReference =
            //    new CodeFieldReferenceExpression(
            //    new CodeThisReferenceExpression(), "widthValue");
            //constructor.Statements.Add(new CodeAssignStatement(widthReference,
            //    new CodeArgumentReferenceExpression("width")));
            //CodeFieldReferenceExpression heightReference =
            //    new CodeFieldReferenceExpression(
            //    new CodeThisReferenceExpression(), "heightValue");
            //constructor.Statements.Add(new CodeAssignStatement(heightReference,
            //    new CodeArgumentReferenceExpression("height")));
            //targetClass.Members.Add(constructor);
        }

        /// <summary>
        /// Add an entry point to the class.
        /// </summary>
        public void AddEntryPoint()
        {
            CodeEntryPointMethod start = new CodeEntryPointMethod();
            CodeObjectCreateExpression objectCreate =
                new CodeObjectCreateExpression(
                new CodeTypeReference("CodeDOMCreatedClass"),
                new CodePrimitiveExpression(5.3),
                new CodePrimitiveExpression(6.9));

            // Add the statement:
            // "CodeDOMCreatedClass testClass = 
            //     new CodeDOMCreatedClass(5.3, 6.9);"
            start.Statements.Add(new CodeVariableDeclarationStatement(
                new CodeTypeReference("CodeDOMCreatedClass"), "testClass",
                objectCreate));

            // Creat the expression:
            // "testClass.ToString()"
            CodeMethodInvokeExpression toStringInvoke =
                new CodeMethodInvokeExpression(
                new CodeVariableReferenceExpression("testClass"), "ToString");

            // Add a System.Console.WriteLine statement with the previous 
            // expression as a parameter.
            start.Statements.Add(new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression("System.Console"),
                "WriteLine", toStringInvoke));
            targetClass.Members.Add(start);
        }

        private CodeDomProvider codeDomProvider = null;

        private CodeDomProvider GetCurrentProvider(string fileExtension)
        {
            CodeDomProvider provider;
            switch (fileExtension)
            {
                case ".cs":
                    provider = CodeDomProvider.CreateProvider("CSharp");
                    break;
                case ".vb":
                    provider = CodeDomProvider.CreateProvider("VisualBasic");
                    break;
                case ".js":
                    provider = CodeDomProvider.CreateProvider("JScript");
                    break;
                default:
                    provider = CodeDomProvider.CreateProvider("CSharp");
                    break;
            }
            return provider;
        }

        private string GetCurrentSelectedProjectType()
        {
            string strProjectSuffix = string.Empty;
            string projecFileExtension = string.Empty;
            int indexOfDot = 0;

            EnvDTE.Project project = VSEnvUtility.GetSelectedProjectHierarchyInstance();
            indexOfDot = project.FullName.IndexOf('.');
            strProjectSuffix = project.FullName.Substring(indexOfDot);

            switch (strProjectSuffix)
            {
                case ".csproj":
                    projecFileExtension = ".cs";
                    break;
                case ".vbproj":
                    projecFileExtension = ".vb";
                    break;
                case ".jsproj":
                    projecFileExtension = ".js";
                    break;
                default:
                    projecFileExtension = ".cs";
                    break;
            }

            return projecFileExtension;
        }
        /// <summary>
        /// Generate CSharp source code from the compile unit.
        /// </summary>
        /// <param name="filename">Output file name</param>
        public void GenerateCSharpCode(string fileName)
        {
            //CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
            string strProjectFileExtension = GetCurrentSelectedProjectType();
            CodeDomProvider provider = GetCurrentProvider(strProjectFileExtension);
            CodeGeneratorOptions options = new CodeGeneratorOptions();
            options.BracingStyle = "C";
            using (StreamWriter sourceWriter = new StreamWriter(fileName))
            {
                provider.GenerateCodeFromCompileUnit(targetUnit, sourceWriter, options);
            }
        }

        /// <summary>
        /// Create the CodeDOM graph and generate the code.
        /// </summary>
        //static void Main()
        //{
        //    Sample sample = new Sample();
        //    sample.AddFields();
        //    sample.AddProperties();
        //    sample.AddMethod();
        //    sample.AddConstructor();
        //    sample.AddEntryPoint();
        //    sample.GenerateCSharpCode(outputFileName);
        //}

        //protected override string GetDefaultExtension(out string pbstrDefaultExtension)
        //{
        //    //throw new NotImplementedException();
        //    string defExt = string.Empty;
        //    pbstrDefaultExtension = string.Empty;

        //    //defExt = this.GetCodeProvider().FileExtension;
            

        //    if (((defExt != null) && (defExt.Length > 0)) && (defExt[0] != '.'))
        //    {
        //        defExt = "." + defExt;
        //    }

        //    if (!string.IsNullOrEmpty(defExt))
        //    {
        //        pbstrDefaultExtension = ".Designer" + defExt;
        //    }

        //    return pbstrDefaultExtension;
        //}

        protected override byte[] GenerateCode(string inputFileContent)
        {
            throw new NotImplementedException();
        }

        protected override string GetDefaultExtension()
        {
            throw new NotImplementedException();
        }
    }
}
